/*
 * Compiler.cpp
 *
 *  Created on: 1 Nov 2010
 *      Author: kn308
 */

#include <cstdio>
#include <iostream>
#include <stdexcept>
#include <csignal>

#include "MAlice.hpp"
#include "MAliceDriver.hpp"
#include "Parser/MAliceParser.hpp"
#include "Scanner/MAliceScanner.hpp"

#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/PassManager.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetSelect.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Target/TargetSelect.h"
#include "llvm/CodeGen/DwarfWriter.h"
#include <llvm/TypeSymbolTable.h>
#include <llvm/ExecutionEngine/JITMemoryManager.h>

#include "Ast/Ast.h"
#include "Ast/ErrorAst.h"
#include "Ast/ExpressionAst.h"
#include "Ast/IntegerLiteralAst.h"
#include "Ast/CharacterLiteralAst.h"
#include "Ast/BinaryOperatorAst.h"
#include "Ast/BinaryAddOperatorAst.h"
#include "Ast/BinaryMulOperatorAst.h"
#include "Ast/BinarySubOperatorAst.h"
#include "Ast/BinaryDivOperatorAst.h"
#include "Ast/BinaryModOperatorAst.h"
#include "Ast/BinaryAndOperatorAst.h"
#include "Ast/BinaryXorOperatorAst.h"
#include "Ast/BinaryOrOperatorAst.h"
#include "Ast/AssignmentOperatorAst.h"

#include "Ast/UnaryAteOperatorAst.h"
#include "Ast/UnaryDrankOperatorAst.h"
#include "Ast/UnaryNegOperatorAst.h"
#include "Ast/UnaryNotOperatorAst.h"
#include "Ast/FloatLiteralAst.h"

#include "Ast/ReturnExpressionAst.h"
#include "Ast/VariableDeclAst.h"
#include "Ast/VariableAst.h"
#include "Ast/TypeAst.h"

char llvm::DwarfWriter::ID = 0;

bool llvm::DwarfWriter::ShouldEmitDwarfDebug() const
{
	return false;
}

void throw_segfault(int fuck)
{
	throw runtime_error("Segmentation Fault");
}


using namespace yy;

int main(int argc, char** argv)
{
	using namespace MAlice;

	signal(SIGSEGV, &throw_segfault);
	try
	{
		InitializeNativeTarget();

		Scanner scanner;
		MAliceDriver driver(&scanner);
		MAliceParser parser(driver);
		parser.set_debug_level(1);
		parser.set_debug_stream(cout);

		Codegen* g = Codegen::get();
		Builder* builder = g->getBuilder();

		std::string e;
		ExecutionEngine *jit;
		jit = EngineBuilder(g->getModule())
				.setCodeModel(CodeModel::Default)
				.setOptLevel(CodeGenOpt::Default)
				.setEngineKind(EngineKind::JIT)
				.setErrorStr(&e)
				.create();
		if (!jit)
		{
			fprintf(stderr, "Could not create ExecutionEngine: %s\n", e.c_str());
			exit(1);
		}

		FunctionPassManager fpm(g->getModule());
		fpm.add(new TargetData(*jit->getTargetData()));
		fpm.add(createDeadCodeEliminationPass());
		fpm.add(createDeadStoreEliminationPass());
		fpm.add(createAggressiveDCEPass());
		fpm.add(createScalarReplAggregatesPass());
		fpm.add(createInstructionCombiningPass());
		fpm.add(createPromoteMemoryToRegisterPass());
		fpm.add(createDemoteRegisterToMemoryPass());
		fpm.add(createReassociatePass());
		fpm.add(createTailDuplicationPass());
		fpm.add(createJumpThreadingPass());
		fpm.add(createCFGSimplificationPass());
		fpm.add(createBreakCriticalEdgesPass());
		fpm.add(createTailCallEliminationPass());
		fpm.add(createLowerSwitchPass());
		fpm.add(createLowerInvokePass());
		fpm.add(createBlockPlacementPass());
		fpm.add(createGVNPass());
		fpm.add(createMemCpyOptPass());
		fpm.add(createSimplifyLibCallsPass());
		fpm.add(createSimplifyHalfPowrLibCallsPass());
		fpm.add(createCodeGenPreparePass());
		fpm.add(createInstructionNamerPass());
		fpm.add(createSSIPass());
		fpm.add(createSSIEverythingPass());
		fpm.add(createGEPSplitterPass());
		fpm.add(createSCCVNPass());
		fpm.add(createABCDPass());
		fpm.add(createConstantPropagationPass());
		fpm.add(createSCCPPass());
		fpm.doInitialization();

		g->getModule()->getTypeSymbolTable().insert("number", Type::getInt32Ty(g->getContext()));
		g->getModule()->getTypeSymbolTable().insert("letter", Type::getInt8Ty(g->getContext()));

		vector<const Type*> args;
		args.push_back(Type::getInt32Ty(g->getContext()));
		args.push_back(Type::getInt8PtrTy(g->getContext()));

		FunctionType *FT = FunctionType::get(Type::getInt32Ty(g->getContext()), args, false);
		Function *F = Function::Create(FT, Function::ExternalLinkage, "alice", g->getModule());
		builder->SetInsertPoint(BasicBlock::Create(g->getContext(), "entry", F));

		parser.parse();

		driver.get()->generate();

		fpm.doFinalization();

		g->getModule()->dump();

	} catch (std::exception& e)
	{
		std::cerr << "Exception: " << e.what() << std::endl;
	}

	return 0;
}
